{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 测试function机制"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step 1: 定义函数接口"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "functions = [\n",
    "    {\n",
    "        \"type\": \"function\",\n",
    "        \"function\": {\n",
    "            \"name\": \"fetch_weather\",\n",
    "            \"description\": \"获取指定城市的天气信息\",\n",
    "            \"parameters\": {\n",
    "                \"type\": \"object\",\n",
    "                \"properties\": {\n",
    "                    \"city\": {\n",
    "                        \"type\": \"string\",\n",
    "                        \"description\": \"目标城市的名称\"\n",
    "                    }\n",
    "                },\n",
    "                \"required\": [\"city\"]\n",
    "            }\n",
    "        }\n",
    "    }\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step 2: 模型处理用户输入"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from openai import OpenAI\n",
    "\n",
    "client = OpenAI(api_key=\"sk-6e21c4400da44725b7ecadf97777d86e\", base_url=\"https://api.deepseek.com\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatCompletion(id='75058804-28f4-4c31-a29b-c7fb463dc4e7', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content='', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_1e6d75c8-9a8a-4f56-8307-10a66d82b60b', function=Function(arguments='{\"city\":\"北京\"}', name='fetch_weather'), type='function', index=0)]))], created=1736933643, model='deepseek-chat', object='chat.completion', service_tier=None, system_fingerprint='fp_3a5770e1b4', usage=CompletionUsage(completion_tokens=19, prompt_tokens=113, total_tokens=132, completion_tokens_details=None, prompt_tokens_details=None, prompt_cache_hit_tokens=64, prompt_cache_miss_tokens=49))"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = client.chat.completions.create(\n",
    "    model=\"deepseek-chat\",\n",
    "    messages=[{\"role\": \"user\", \"content\": \"查询北京的天气\"}],\n",
    "    tools=functions,\n",
    "    stream=False,\n",
    "    tool_choice=\"auto\"  # 自动判断是否需要调用函数\n",
    ")\n",
    "\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tool_calls\n"
     ]
    }
   ],
   "source": [
    "print(response.choices[0].finish_reason)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Function(arguments='{\"city\":\"北京\"}', name='fetch_weather')\n"
     ]
    }
   ],
   "source": [
    "print(response.choices[0].message.tool_calls[0].function)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step 3: 获取 function_call"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "需要调用的函数: fetch_weather\n",
      "函数参数: {\"city\":\"北京\"}\n"
     ]
    }
   ],
   "source": [
    "if response.choices[0].finish_reason == \"tool_calls\":\n",
    "    tool_call = response.choices[0].message.tool_calls[0]\n",
    "    if tool_call.type == \"function\":\n",
    "        function_call = tool_call.function\n",
    "        function_name = function_call.name\n",
    "        function_arguments = function_call.arguments\n",
    "        print(f\"需要调用的函数: {function_name}\")\n",
    "        print(f\"函数参数: {function_arguments}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step 4: 后端执行函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'city': '北京', 'temperature': 25}\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "\n",
    "def fetch_weather(city):\n",
    "    # 模拟返回天气数据\n",
    "    return {\"city\": city, \"temperature\": 25}\n",
    "\n",
    "# 执行函数\n",
    "arguments = json.loads(function_arguments)\n",
    "result = fetch_weather(**arguments)\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step 5: 将结果返回给模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Function(arguments='{\"city\":\"北京\"}', name='fetch_weather')\n"
     ]
    }
   ],
   "source": [
    "print(function_call)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "Object of type Function is not JSON serializable",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[19], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m response_with_result \u001b[38;5;241m=\u001b[39m client\u001b[38;5;241m.\u001b[39mchat\u001b[38;5;241m.\u001b[39mcompletions\u001b[38;5;241m.\u001b[39mcreate(\n\u001b[0;32m      2\u001b[0m     model\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdeepseek-chat\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m      3\u001b[0m     messages\u001b[38;5;241m=\u001b[39m[\n\u001b[0;32m      4\u001b[0m         {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrole\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124muser\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m查询北京的天气\u001b[39m\u001b[38;5;124m\"\u001b[39m},\n\u001b[0;32m      5\u001b[0m         {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrole\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124massistant\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtool\u001b[39m\u001b[38;5;124m\"\u001b[39m: function_call},\n\u001b[0;32m      6\u001b[0m         {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrole\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfunction\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mname\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfetch_weather\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent\u001b[39m\u001b[38;5;124m\"\u001b[39m: json\u001b[38;5;241m.\u001b[39mdumps(result)}\n\u001b[0;32m      7\u001b[0m     ]\n\u001b[0;32m      8\u001b[0m )\n\u001b[0;32m     10\u001b[0m \u001b[38;5;66;03m# 获取模型最终生成的自然语言响应\u001b[39;00m\n\u001b[0;32m     11\u001b[0m final_response \u001b[38;5;241m=\u001b[39m response_with_result\u001b[38;5;241m.\u001b[39mchoices[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;241m.\u001b[39mmessage[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent\u001b[39m\u001b[38;5;124m\"\u001b[39m]\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python311\\site-packages\\openai\\_utils\\_utils.py:279\u001b[0m, in \u001b[0;36mrequired_args.<locals>.inner.<locals>.wrapper\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m    277\u001b[0m             msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mMissing required argument: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mquote(missing[\u001b[38;5;241m0\u001b[39m])\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m    278\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(msg)\n\u001b[1;32m--> 279\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m func(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python311\\site-packages\\openai\\resources\\chat\\completions.py:859\u001b[0m, in \u001b[0;36mCompletions.create\u001b[1;34m(self, messages, model, audio, frequency_penalty, function_call, functions, logit_bias, logprobs, max_completion_tokens, max_tokens, metadata, modalities, n, parallel_tool_calls, prediction, presence_penalty, reasoning_effort, response_format, seed, service_tier, stop, store, stream, stream_options, temperature, tool_choice, tools, top_logprobs, top_p, user, extra_headers, extra_query, extra_body, timeout)\u001b[0m\n\u001b[0;32m    817\u001b[0m \u001b[38;5;129m@required_args\u001b[39m([\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmodel\u001b[39m\u001b[38;5;124m\"\u001b[39m], [\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmodel\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstream\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n\u001b[0;32m    818\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcreate\u001b[39m(\n\u001b[0;32m    819\u001b[0m     \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    856\u001b[0m     timeout: \u001b[38;5;28mfloat\u001b[39m \u001b[38;5;241m|\u001b[39m httpx\u001b[38;5;241m.\u001b[39mTimeout \u001b[38;5;241m|\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;241m|\u001b[39m NotGiven \u001b[38;5;241m=\u001b[39m NOT_GIVEN,\n\u001b[0;32m    857\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ChatCompletion \u001b[38;5;241m|\u001b[39m Stream[ChatCompletionChunk]:\n\u001b[0;32m    858\u001b[0m     validate_response_format(response_format)\n\u001b[1;32m--> 859\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_post(\n\u001b[0;32m    860\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m/chat/completions\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m    861\u001b[0m         body\u001b[38;5;241m=\u001b[39mmaybe_transform(\n\u001b[0;32m    862\u001b[0m             {\n\u001b[0;32m    863\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m: messages,\n\u001b[0;32m    864\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmodel\u001b[39m\u001b[38;5;124m\"\u001b[39m: model,\n\u001b[0;32m    865\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124maudio\u001b[39m\u001b[38;5;124m\"\u001b[39m: audio,\n\u001b[0;32m    866\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfrequency_penalty\u001b[39m\u001b[38;5;124m\"\u001b[39m: frequency_penalty,\n\u001b[0;32m    867\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfunction_call\u001b[39m\u001b[38;5;124m\"\u001b[39m: function_call,\n\u001b[0;32m    868\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfunctions\u001b[39m\u001b[38;5;124m\"\u001b[39m: functions,\n\u001b[0;32m    869\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlogit_bias\u001b[39m\u001b[38;5;124m\"\u001b[39m: logit_bias,\n\u001b[0;32m    870\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlogprobs\u001b[39m\u001b[38;5;124m\"\u001b[39m: logprobs,\n\u001b[0;32m    871\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmax_completion_tokens\u001b[39m\u001b[38;5;124m\"\u001b[39m: max_completion_tokens,\n\u001b[0;32m    872\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmax_tokens\u001b[39m\u001b[38;5;124m\"\u001b[39m: max_tokens,\n\u001b[0;32m    873\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmetadata\u001b[39m\u001b[38;5;124m\"\u001b[39m: metadata,\n\u001b[0;32m    874\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmodalities\u001b[39m\u001b[38;5;124m\"\u001b[39m: modalities,\n\u001b[0;32m    875\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mn\u001b[39m\u001b[38;5;124m\"\u001b[39m: n,\n\u001b[0;32m    876\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mparallel_tool_calls\u001b[39m\u001b[38;5;124m\"\u001b[39m: parallel_tool_calls,\n\u001b[0;32m    877\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mprediction\u001b[39m\u001b[38;5;124m\"\u001b[39m: prediction,\n\u001b[0;32m    878\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mpresence_penalty\u001b[39m\u001b[38;5;124m\"\u001b[39m: presence_penalty,\n\u001b[0;32m    879\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mreasoning_effort\u001b[39m\u001b[38;5;124m\"\u001b[39m: reasoning_effort,\n\u001b[0;32m    880\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresponse_format\u001b[39m\u001b[38;5;124m\"\u001b[39m: response_format,\n\u001b[0;32m    881\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mseed\u001b[39m\u001b[38;5;124m\"\u001b[39m: seed,\n\u001b[0;32m    882\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mservice_tier\u001b[39m\u001b[38;5;124m\"\u001b[39m: service_tier,\n\u001b[0;32m    883\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstop\u001b[39m\u001b[38;5;124m\"\u001b[39m: stop,\n\u001b[0;32m    884\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstore\u001b[39m\u001b[38;5;124m\"\u001b[39m: store,\n\u001b[0;32m    885\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstream\u001b[39m\u001b[38;5;124m\"\u001b[39m: stream,\n\u001b[0;32m    886\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstream_options\u001b[39m\u001b[38;5;124m\"\u001b[39m: stream_options,\n\u001b[0;32m    887\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtemperature\u001b[39m\u001b[38;5;124m\"\u001b[39m: temperature,\n\u001b[0;32m    888\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtool_choice\u001b[39m\u001b[38;5;124m\"\u001b[39m: tool_choice,\n\u001b[0;32m    889\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtools\u001b[39m\u001b[38;5;124m\"\u001b[39m: tools,\n\u001b[0;32m    890\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtop_logprobs\u001b[39m\u001b[38;5;124m\"\u001b[39m: top_logprobs,\n\u001b[0;32m    891\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtop_p\u001b[39m\u001b[38;5;124m\"\u001b[39m: top_p,\n\u001b[0;32m    892\u001b[0m                 \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124muser\u001b[39m\u001b[38;5;124m\"\u001b[39m: user,\n\u001b[0;32m    893\u001b[0m             },\n\u001b[0;32m    894\u001b[0m             completion_create_params\u001b[38;5;241m.\u001b[39mCompletionCreateParams,\n\u001b[0;32m    895\u001b[0m         ),\n\u001b[0;32m    896\u001b[0m         options\u001b[38;5;241m=\u001b[39mmake_request_options(\n\u001b[0;32m    897\u001b[0m             extra_headers\u001b[38;5;241m=\u001b[39mextra_headers, extra_query\u001b[38;5;241m=\u001b[39mextra_query, extra_body\u001b[38;5;241m=\u001b[39mextra_body, timeout\u001b[38;5;241m=\u001b[39mtimeout\n\u001b[0;32m    898\u001b[0m         ),\n\u001b[0;32m    899\u001b[0m         cast_to\u001b[38;5;241m=\u001b[39mChatCompletion,\n\u001b[0;32m    900\u001b[0m         stream\u001b[38;5;241m=\u001b[39mstream \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m,\n\u001b[0;32m    901\u001b[0m         stream_cls\u001b[38;5;241m=\u001b[39mStream[ChatCompletionChunk],\n\u001b[0;32m    902\u001b[0m     )\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python311\\site-packages\\openai\\_base_client.py:1283\u001b[0m, in \u001b[0;36mSyncAPIClient.post\u001b[1;34m(self, path, cast_to, body, options, files, stream, stream_cls)\u001b[0m\n\u001b[0;32m   1269\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mpost\u001b[39m(\n\u001b[0;32m   1270\u001b[0m     \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m   1271\u001b[0m     path: \u001b[38;5;28mstr\u001b[39m,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m   1278\u001b[0m     stream_cls: \u001b[38;5;28mtype\u001b[39m[_StreamT] \u001b[38;5;241m|\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[0;32m   1279\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ResponseT \u001b[38;5;241m|\u001b[39m _StreamT:\n\u001b[0;32m   1280\u001b[0m     opts \u001b[38;5;241m=\u001b[39m FinalRequestOptions\u001b[38;5;241m.\u001b[39mconstruct(\n\u001b[0;32m   1281\u001b[0m         method\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mpost\u001b[39m\u001b[38;5;124m\"\u001b[39m, url\u001b[38;5;241m=\u001b[39mpath, json_data\u001b[38;5;241m=\u001b[39mbody, files\u001b[38;5;241m=\u001b[39mto_httpx_files(files), \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39moptions\n\u001b[0;32m   1282\u001b[0m     )\n\u001b[1;32m-> 1283\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m cast(ResponseT, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrequest(cast_to, opts, stream\u001b[38;5;241m=\u001b[39mstream, stream_cls\u001b[38;5;241m=\u001b[39mstream_cls))\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python311\\site-packages\\openai\\_base_client.py:960\u001b[0m, in \u001b[0;36mSyncAPIClient.request\u001b[1;34m(self, cast_to, options, remaining_retries, stream, stream_cls)\u001b[0m\n\u001b[0;32m    957\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m    958\u001b[0m     retries_taken \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m--> 960\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_request(\n\u001b[0;32m    961\u001b[0m     cast_to\u001b[38;5;241m=\u001b[39mcast_to,\n\u001b[0;32m    962\u001b[0m     options\u001b[38;5;241m=\u001b[39moptions,\n\u001b[0;32m    963\u001b[0m     stream\u001b[38;5;241m=\u001b[39mstream,\n\u001b[0;32m    964\u001b[0m     stream_cls\u001b[38;5;241m=\u001b[39mstream_cls,\n\u001b[0;32m    965\u001b[0m     retries_taken\u001b[38;5;241m=\u001b[39mretries_taken,\n\u001b[0;32m    966\u001b[0m )\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python311\\site-packages\\openai\\_base_client.py:986\u001b[0m, in \u001b[0;36mSyncAPIClient._request\u001b[1;34m(self, cast_to, options, retries_taken, stream, stream_cls)\u001b[0m\n\u001b[0;32m    983\u001b[0m options \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_prepare_options(options)\n\u001b[0;32m    985\u001b[0m remaining_retries \u001b[38;5;241m=\u001b[39m options\u001b[38;5;241m.\u001b[39mget_max_retries(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmax_retries) \u001b[38;5;241m-\u001b[39m retries_taken\n\u001b[1;32m--> 986\u001b[0m request \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_build_request(options, retries_taken\u001b[38;5;241m=\u001b[39mretries_taken)\n\u001b[0;32m    987\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_prepare_request(request)\n\u001b[0;32m    989\u001b[0m kwargs: HttpxSendArgs \u001b[38;5;241m=\u001b[39m {}\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python311\\site-packages\\openai\\_base_client.py:506\u001b[0m, in \u001b[0;36mBaseClient._build_request\u001b[1;34m(self, options, retries_taken)\u001b[0m\n\u001b[0;32m    503\u001b[0m     kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mextensions\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msni_hostname\u001b[39m\u001b[38;5;124m\"\u001b[39m: prepared_url\u001b[38;5;241m.\u001b[39mhost\u001b[38;5;241m.\u001b[39mreplace(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m_\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m-\u001b[39m\u001b[38;5;124m\"\u001b[39m)}\n\u001b[0;32m    505\u001b[0m \u001b[38;5;66;03m# TODO: report this error to httpx\u001b[39;00m\n\u001b[1;32m--> 506\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_client\u001b[38;5;241m.\u001b[39mbuild_request(  \u001b[38;5;66;03m# pyright: ignore[reportUnknownMemberType]\u001b[39;00m\n\u001b[0;32m    507\u001b[0m     headers\u001b[38;5;241m=\u001b[39mheaders,\n\u001b[0;32m    508\u001b[0m     timeout\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtimeout \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(options\u001b[38;5;241m.\u001b[39mtimeout, NotGiven) \u001b[38;5;28;01melse\u001b[39;00m options\u001b[38;5;241m.\u001b[39mtimeout,\n\u001b[0;32m    509\u001b[0m     method\u001b[38;5;241m=\u001b[39moptions\u001b[38;5;241m.\u001b[39mmethod,\n\u001b[0;32m    510\u001b[0m     url\u001b[38;5;241m=\u001b[39mprepared_url,\n\u001b[0;32m    511\u001b[0m     \u001b[38;5;66;03m# the `Query` type that we use is incompatible with qs'\u001b[39;00m\n\u001b[0;32m    512\u001b[0m     \u001b[38;5;66;03m# `Params` type as it needs to be typed as `Mapping[str, object]`\u001b[39;00m\n\u001b[0;32m    513\u001b[0m     \u001b[38;5;66;03m# so that passing a `TypedDict` doesn't cause an error.\u001b[39;00m\n\u001b[0;32m    514\u001b[0m     \u001b[38;5;66;03m# https://github.com/microsoft/pyright/issues/3526#event-6715453066\u001b[39;00m\n\u001b[0;32m    515\u001b[0m     params\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mqs\u001b[38;5;241m.\u001b[39mstringify(cast(Mapping[\u001b[38;5;28mstr\u001b[39m, Any], params)) \u001b[38;5;28;01mif\u001b[39;00m params \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[0;32m    516\u001b[0m     json\u001b[38;5;241m=\u001b[39mjson_data,\n\u001b[0;32m    517\u001b[0m     files\u001b[38;5;241m=\u001b[39mfiles,\n\u001b[0;32m    518\u001b[0m     \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[0;32m    519\u001b[0m )\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python311\\site-packages\\httpx\\_client.py:378\u001b[0m, in \u001b[0;36mBaseClient.build_request\u001b[1;34m(self, method, url, content, data, files, json, params, headers, cookies, timeout, extensions)\u001b[0m\n\u001b[0;32m    372\u001b[0m     timeout \u001b[38;5;241m=\u001b[39m (\n\u001b[0;32m    373\u001b[0m         \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtimeout\n\u001b[0;32m    374\u001b[0m         \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(timeout, UseClientDefault)\n\u001b[0;32m    375\u001b[0m         \u001b[38;5;28;01melse\u001b[39;00m Timeout(timeout)\n\u001b[0;32m    376\u001b[0m     )\n\u001b[0;32m    377\u001b[0m     extensions \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mdict\u001b[39m(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mextensions, timeout\u001b[38;5;241m=\u001b[39mtimeout\u001b[38;5;241m.\u001b[39mas_dict())\n\u001b[1;32m--> 378\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Request(\n\u001b[0;32m    379\u001b[0m     method,\n\u001b[0;32m    380\u001b[0m     url,\n\u001b[0;32m    381\u001b[0m     content\u001b[38;5;241m=\u001b[39mcontent,\n\u001b[0;32m    382\u001b[0m     data\u001b[38;5;241m=\u001b[39mdata,\n\u001b[0;32m    383\u001b[0m     files\u001b[38;5;241m=\u001b[39mfiles,\n\u001b[0;32m    384\u001b[0m     json\u001b[38;5;241m=\u001b[39mjson,\n\u001b[0;32m    385\u001b[0m     params\u001b[38;5;241m=\u001b[39mparams,\n\u001b[0;32m    386\u001b[0m     headers\u001b[38;5;241m=\u001b[39mheaders,\n\u001b[0;32m    387\u001b[0m     cookies\u001b[38;5;241m=\u001b[39mcookies,\n\u001b[0;32m    388\u001b[0m     extensions\u001b[38;5;241m=\u001b[39mextensions,\n\u001b[0;32m    389\u001b[0m )\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python311\\site-packages\\httpx\\_models.py:408\u001b[0m, in \u001b[0;36mRequest.__init__\u001b[1;34m(self, method, url, params, headers, cookies, content, data, files, json, stream, extensions)\u001b[0m\n\u001b[0;32m    406\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m stream \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m    407\u001b[0m     content_type: \u001b[38;5;28mstr\u001b[39m \u001b[38;5;241m|\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mheaders\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent-type\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m--> 408\u001b[0m     headers, stream \u001b[38;5;241m=\u001b[39m encode_request(\n\u001b[0;32m    409\u001b[0m         content\u001b[38;5;241m=\u001b[39mcontent,\n\u001b[0;32m    410\u001b[0m         data\u001b[38;5;241m=\u001b[39mdata,\n\u001b[0;32m    411\u001b[0m         files\u001b[38;5;241m=\u001b[39mfiles,\n\u001b[0;32m    412\u001b[0m         json\u001b[38;5;241m=\u001b[39mjson,\n\u001b[0;32m    413\u001b[0m         boundary\u001b[38;5;241m=\u001b[39mget_multipart_boundary_from_content_type(\n\u001b[0;32m    414\u001b[0m             content_type\u001b[38;5;241m=\u001b[39mcontent_type\u001b[38;5;241m.\u001b[39mencode(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mheaders\u001b[38;5;241m.\u001b[39mencoding)\n\u001b[0;32m    415\u001b[0m             \u001b[38;5;28;01mif\u001b[39;00m content_type\n\u001b[0;32m    416\u001b[0m             \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m    417\u001b[0m         ),\n\u001b[0;32m    418\u001b[0m     )\n\u001b[0;32m    419\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_prepare(headers)\n\u001b[0;32m    420\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstream \u001b[38;5;241m=\u001b[39m stream\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python311\\site-packages\\httpx\\_content.py:216\u001b[0m, in \u001b[0;36mencode_request\u001b[1;34m(content, data, files, json, boundary)\u001b[0m\n\u001b[0;32m    214\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m encode_urlencoded_data(data)\n\u001b[0;32m    215\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m json \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m--> 216\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m encode_json(json)\n\u001b[0;32m    218\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m {}, ByteStream(\u001b[38;5;124mb\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python311\\site-packages\\httpx\\_content.py:177\u001b[0m, in \u001b[0;36mencode_json\u001b[1;34m(json)\u001b[0m\n\u001b[0;32m    176\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mencode_json\u001b[39m(json: Any) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, \u001b[38;5;28mstr\u001b[39m], ByteStream]:\n\u001b[1;32m--> 177\u001b[0m     body \u001b[38;5;241m=\u001b[39m json_dumps(\n\u001b[0;32m    178\u001b[0m         json, ensure_ascii\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m, separators\u001b[38;5;241m=\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m,\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m:\u001b[39m\u001b[38;5;124m\"\u001b[39m), allow_nan\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[0;32m    179\u001b[0m     )\u001b[38;5;241m.\u001b[39mencode(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mutf-8\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m    180\u001b[0m     content_length \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mstr\u001b[39m(\u001b[38;5;28mlen\u001b[39m(body))\n\u001b[0;32m    181\u001b[0m     content_type \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mapplication/json\u001b[39m\u001b[38;5;124m\"\u001b[39m\n",
      "File \u001b[1;32md:\\ProgramData\\anaconda3\\envs\\langflow\\Lib\\json\\__init__.py:238\u001b[0m, in \u001b[0;36mdumps\u001b[1;34m(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)\u001b[0m\n\u001b[0;32m    232\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mcls\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m    233\u001b[0m     \u001b[38;5;28mcls\u001b[39m \u001b[38;5;241m=\u001b[39m JSONEncoder\n\u001b[0;32m    234\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mcls\u001b[39m(\n\u001b[0;32m    235\u001b[0m     skipkeys\u001b[38;5;241m=\u001b[39mskipkeys, ensure_ascii\u001b[38;5;241m=\u001b[39mensure_ascii,\n\u001b[0;32m    236\u001b[0m     check_circular\u001b[38;5;241m=\u001b[39mcheck_circular, allow_nan\u001b[38;5;241m=\u001b[39mallow_nan, indent\u001b[38;5;241m=\u001b[39mindent,\n\u001b[0;32m    237\u001b[0m     separators\u001b[38;5;241m=\u001b[39mseparators, default\u001b[38;5;241m=\u001b[39mdefault, sort_keys\u001b[38;5;241m=\u001b[39msort_keys,\n\u001b[1;32m--> 238\u001b[0m     \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkw)\u001b[38;5;241m.\u001b[39mencode(obj)\n",
      "File \u001b[1;32md:\\ProgramData\\anaconda3\\envs\\langflow\\Lib\\json\\encoder.py:200\u001b[0m, in \u001b[0;36mJSONEncoder.encode\u001b[1;34m(self, o)\u001b[0m\n\u001b[0;32m    196\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m encode_basestring(o)\n\u001b[0;32m    197\u001b[0m \u001b[38;5;66;03m# This doesn't pass the iterator directly to ''.join() because the\u001b[39;00m\n\u001b[0;32m    198\u001b[0m \u001b[38;5;66;03m# exceptions aren't as detailed.  The list call should be roughly\u001b[39;00m\n\u001b[0;32m    199\u001b[0m \u001b[38;5;66;03m# equivalent to the PySequence_Fast that ''.join() would do.\u001b[39;00m\n\u001b[1;32m--> 200\u001b[0m chunks \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39miterencode(o, _one_shot\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[0;32m    201\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(chunks, (\u001b[38;5;28mlist\u001b[39m, \u001b[38;5;28mtuple\u001b[39m)):\n\u001b[0;32m    202\u001b[0m     chunks \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(chunks)\n",
      "File \u001b[1;32md:\\ProgramData\\anaconda3\\envs\\langflow\\Lib\\json\\encoder.py:258\u001b[0m, in \u001b[0;36mJSONEncoder.iterencode\u001b[1;34m(self, o, _one_shot)\u001b[0m\n\u001b[0;32m    253\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m    254\u001b[0m     _iterencode \u001b[38;5;241m=\u001b[39m _make_iterencode(\n\u001b[0;32m    255\u001b[0m         markers, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdefault, _encoder, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mindent, floatstr,\n\u001b[0;32m    256\u001b[0m         \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mkey_separator, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mitem_separator, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msort_keys,\n\u001b[0;32m    257\u001b[0m         \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mskipkeys, _one_shot)\n\u001b[1;32m--> 258\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m _iterencode(o, \u001b[38;5;241m0\u001b[39m)\n",
      "File \u001b[1;32md:\\ProgramData\\anaconda3\\envs\\langflow\\Lib\\json\\encoder.py:180\u001b[0m, in \u001b[0;36mJSONEncoder.default\u001b[1;34m(self, o)\u001b[0m\n\u001b[0;32m    161\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mdefault\u001b[39m(\u001b[38;5;28mself\u001b[39m, o):\n\u001b[0;32m    162\u001b[0m \u001b[38;5;250m    \u001b[39m\u001b[38;5;124;03m\"\"\"Implement this method in a subclass such that it returns\u001b[39;00m\n\u001b[0;32m    163\u001b[0m \u001b[38;5;124;03m    a serializable object for ``o``, or calls the base implementation\u001b[39;00m\n\u001b[0;32m    164\u001b[0m \u001b[38;5;124;03m    (to raise a ``TypeError``).\u001b[39;00m\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    178\u001b[0m \n\u001b[0;32m    179\u001b[0m \u001b[38;5;124;03m    \"\"\"\u001b[39;00m\n\u001b[1;32m--> 180\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mObject of type \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mo\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__class__\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m    181\u001b[0m                     \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mis not JSON serializable\u001b[39m\u001b[38;5;124m'\u001b[39m)\n",
      "\u001b[1;31mTypeError\u001b[0m: Object of type Function is not JSON serializable"
     ]
    }
   ],
   "source": [
    "response_with_result = client.chat.completions.create(\n",
    "    model=\"deepseek-chat\",\n",
    "    messages=[\n",
    "        {\"role\": \"user\", \"content\": \"查询北京的天气\"},\n",
    "        {\"role\": \"assistant\", \"content\": None, \"tool\": function_call},\n",
    "        {\"role\": \"function\", \"name\": \"fetch_weather\", \"content\": json.dumps(result)}\n",
    "    ]\n",
    ")\n",
    "\n",
    "# 获取模型最终生成的自然语言响应\n",
    "final_response = response_with_result.choices[0].message[\"content\"]\n",
    "print(final_response)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DeepSeek 官网示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "from openai import OpenAI\n",
    "\n",
    "def send_messages(messages):\n",
    "    response = client.chat.completions.create(\n",
    "        model=\"deepseek-chat\",\n",
    "        messages=messages,\n",
    "        tools=tools\n",
    "    )\n",
    "    return response.choices[0].message\n",
    "\n",
    "client = OpenAI(\n",
    "    api_key=\"sk-6e21c4400da44725b7ecadf97777d86e\",\n",
    "    base_url=\"https://api.deepseek.com\",\n",
    ")\n",
    "\n",
    "tools = [\n",
    "    {\n",
    "        \"type\": \"function\",\n",
    "        \"function\": {\n",
    "            \"name\": \"get_weather\",\n",
    "            \"description\": \"Get weather of an location, the user shoud supply a location first\",\n",
    "            \"parameters\": {\n",
    "                \"type\": \"object\",\n",
    "                \"properties\": {\n",
    "                    \"location\": {\n",
    "                        \"type\": \"string\",\n",
    "                        \"description\": \"The city and state, e.g. San Francisco, CA\",\n",
    "                    }\n",
    "                },\n",
    "                \"required\": [\"location\"]\n",
    "            },\n",
    "        }\n",
    "    },\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. 用户：询问现在的天气"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "User>\t How's the weather in Hangzhou?\n"
     ]
    }
   ],
   "source": [
    "messages = [{\"role\": \"user\", \"content\": \"How's the weather in Hangzhou?\"}]\n",
    "print(f\"User>\\t {messages[0]['content']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. 模型：返回 function get_weather({location: 'Hangzhou'})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ChatCompletionMessage(content='', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_05c7e3d6-d363-4994-a396-920349f5108e', function=Function(arguments='{\"location\":\"Hangzhou\"}', name='get_weather'), type='function', index=0)])\n"
     ]
    }
   ],
   "source": [
    "message = send_messages(messages)\n",
    "print(message)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. 用户：调用 function get_weather({location: 'Hangzhou'})，并传给模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "User>\t [{'role': 'user', 'content': \"How's the weather in Hangzhou?\"}, ChatCompletionMessage(content='', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_05c7e3d6-d363-4994-a396-920349f5108e', function=Function(arguments='{\"location\":\"Hangzhou\"}', name='get_weather'), type='function', index=0)]), {'role': 'tool', 'tool_call_id': 'call_0_05c7e3d6-d363-4994-a396-920349f5108e', 'content': '24℃'}]\n"
     ]
    }
   ],
   "source": [
    "tool = message.tool_calls[0]\n",
    "messages.append(message)\n",
    "messages.append({\"role\": \"tool\", \"tool_call_id\": tool.id, \"content\": \"24℃\"})\n",
    "print(f\"User>\\t {messages}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. 模型：返回自然语言，\"The current temperature in Hangzhou is 24°C.\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "message = send_messages(messages)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model>\t ChatCompletionMessage(content='', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=None)\n"
     ]
    }
   ],
   "source": [
    "print(f\"Model>\\t {message}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "发现官网的代码有点问题，最后一次调用不能加tools=tools，否则返回的是空的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ChatCompletion(id='5a1a4982-f62b-4398-95f0-ebff52429ccf', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content=\"The current weather in Hangzhou is 24°C. For more specific details like humidity, wind speed, or forecasts, please let me know if you'd like me to check further!\", refusal=None, role='assistant', audio=None, function_call=None, tool_calls=None))], created=1736996310, model='deepseek-chat', object='chat.completion', service_tier=None, system_fingerprint='fp_3a5770e1b4', usage=CompletionUsage(completion_tokens=38, prompt_tokens=39, total_tokens=77, completion_tokens_details=None, prompt_tokens_details=None, prompt_cache_hit_tokens=0, prompt_cache_miss_tokens=39))\n"
     ]
    }
   ],
   "source": [
    "result = client.chat.completions.create(\n",
    "        model=\"deepseek-chat\",\n",
    "        messages=messages\n",
    "    )\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ChatCompletionMessage(content=\"The current weather in Hangzhou is 24°C. For more specific details like humidity, wind speed, or forecasts, please let me know if you'd like me to check further!\", refusal=None, role='assistant', audio=None, function_call=None, tool_calls=None)\n"
     ]
    }
   ],
   "source": [
    "print(result.choices[0].message)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langflow",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
